#include "StdAfx.h"
#include "TcpConnection.h"

#include <boost/bind.hpp>

#include "TcpPacket.h"

TcpConnection::TcpConnection(boost::asio::io_service& io_service, TcpConnectionEventHandler* eventHandler)
: io_service_(io_service)
, strand_(io_service)
, socketPtr_(new boost::asio::ip::tcp::socket(io_service))
, eventHandler_(eventHandler)
{
}

TcpConnection::TcpConnection(std::shared_ptr<boost::asio::ip::tcp::socket>& socket, TcpConnectionEventHandler* eventHandler)
: io_service_(socket->get_io_service())
, strand_(io_service_)
, socketPtr_(socket)
, eventHandler_(eventHandler)
{
}


TcpConnection::~TcpConnection()
{
	Close();
}

void TcpConnection::Start()
{
	Receive();
}

void TcpConnection::Stop()
{
	strand_.post(boost::bind(&TcpConnection::Close, this));
}

void TcpConnection::Close()
{
	socketPtr_->close();
}

void TcpConnection::Send(TcpPacket* packet, size_t packetSize)
{
	if(!socketPtr_->is_open())
		return;

	socketPtr_->async_write_some(
		boost::asio::buffer(packet, packetSize),
		strand_.wrap(boost::bind(&TcpConnection::OnWriteCompleted, this, boost::asio::placeholders::error)));
}

void TcpConnection::Receive()
{
	if(!socketPtr_->is_open())
		return;

	socketPtr_->async_read_some(boost::asio::buffer(buffer_),
		strand_.wrap(boost::bind(&TcpConnection::OnReadCompleted, this, boost::asio::placeholders::error,
		boost::asio::placeholders::bytes_transferred)));
}

void TcpConnection::OnConnectCompleted(const boost::system::error_code& error)
{
	if(!error)
	{
		Start();
	}
	else
	{
#ifdef _DEBUG
		std::cout << "Connection::OnConnectCompleted(), Error: " << error.message() << "\n";
#endif
		socketPtr_->close();
	}

	eventHandler_->OnConnected(this, error.value());
}

void TcpConnection::OnReadCompleted(const boost::system::error_code& error, std::size_t bytes_transferred)
{
	if(error)
	{
		eventHandler_->OnClosed(this, error.value());
	}
	else
	{
		std::deque<TcpPacket*> packets;
		packetProcessor_.Crop(buffer_.data(), bytes_transferred, packets);

		for(std::size_t i = 0; i < packets.size(); ++i)
		{
			eventHandler_->OnReceived(this, packets.at(i), error.value());
		}

		Receive();
	}
}

void TcpConnection::OnWriteCompleted(const boost::system::error_code& error)
{
	if(error)
	{
		eventHandler_->OnClosed(this, error.value());
	}
}
